﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace DataUnitTesting
{
    class Program
    {
        #region time variables
        static long startTime;
        static long endTime;
        static long deltaNanoSec;
        #endregion

        static void Main(string[] args)
        {
            runCasting();
            //runAbstractClassTest();
            //runProjectionTest();
            //runStructClassTest();
            //runArrayTest();
            //runEnumTest();
        }

        #region time accessors
        public static void getStart()
        {
            startTime = DateTime.Now.Ticks * 100;
        }
        public static void getDelta()
        {
            endTime = DateTime.Now.Ticks * 100;
            deltaNanoSec = endTime - startTime;
        }
        public static void writeResult(bool block, string message)
        {
            Console.WriteLine("total time nsec for " + message + " = " + deltaNanoSec);
            if (block)
                Console.ReadLine();
        }
        public static void writeResult(bool block, string message, long dividefactor)
        {
            Console.WriteLine("total time for " + message + " = " + deltaNanoSec + " nsec. Per op = " + deltaNanoSec / dividefactor + " nsec");
            if (block)
            {
                Console.WriteLine("done. press enter.");
                Console.ReadLine();
            }
        }
        public static void write(bool block, string message)
        {
            Console.WriteLine(message);
            if (block)
                Console.ReadLine();
        }

        #endregion

        #region runners
        public static void runCasting()
        {
            #region result
            /* conclusion. no difference between casting premitives and not.
            total time for heaving casting = 5000300 nsec. Per op = 50 nsec
            total time for heaving casting = 4000300 nsec. Per op = 40 nsec
            total time for heaving casting = 5000300 nsec. Per op = 50 nsec
            total time for heaving casting = 3000100 nsec. Per op = 30 nsec
            total time for heaving casting = 4000300 nsec. Per op = 40 nsec
            total time for heaving casting = 4000200 nsec. Per op = 40 nsec
            total time for no casting = 3000200 nsec. Per op = 30 nsec
            total time for no casting = 3000200 nsec. Per op = 30 nsec
            total time for no casting = 4000300 nsec. Per op = 40 nsec
            total time for no casting = 3000100 nsec. Per op = 30 nsec
            total time for no casting = 4000300 nsec. Per op = 40 nsec
            total time for no casting = 4000200 nsec. Per op = 40 nsec
            */
            #endregion

            unitTestCast(false);
            unitTestCast(false);
            unitTestCast(false);
            unitTestCast(false);
            unitTestCast(false);
            unitTestCast(false);
            unitTestNoCast(false);
            unitTestNoCast(false);
            unitTestNoCast(false);
            unitTestNoCast(false);
            unitTestNoCast(false);
            unitTestNoCast(true);
        }

        public static void runAbstractClassTest()
        {
            #region results
            /* Conclusion. Calling an override method is just as fast as calling a non override method
            total time for extended class method = 3000200 nsec. Per op = 100 nsec
            total time for extended class method = 0 nsec. Per op = 0 nsec
            total time for extended class method = 1000100 nsec. Per op = 33 nsec
            total time for extended class method = 1000100 nsec. Per op = 33 nsec
            total time for extended class method = 1000000 nsec. Per op = 33 nsec
            total time for extended class method = 0 nsec. Per op = 0 nsec
            total time for extended class method = 1000100 nsec. Per op = 33 nsec
            total time for extended class method = 1000000 nsec. Per op = 33 nsec
            total time for extended class method = 1000100 nsec. Per op = 33 nsec
            total time for extended class method = 0 nsec. Per op = 0 nsec
            total time for nonextended class method = 1000100 nsec. Per op = 33 nsec
            total time for nonextended class method = 0 nsec. Per op = 0 nsec
            total time for nonextended class method = 2000100 nsec. Per op = 66 nsec
            total time for nonextended class method = 1000100 nsec. Per op = 33 nsec
            total time for nonextended class method = 1000000 nsec. Per op = 33 nsec
            total time for nonextended class method = 0 nsec. Per op = 0 nsec
            total time for nonextended class method = 1000100 nsec. Per op = 33 nsec
            total time for nonextended class method = 2000100 nsec. Per op = 66 nsec
             */
            #endregion
            unitTestOverride(false);
            unitTestOverride(false);
            unitTestOverride(false);
            unitTestOverride(false);
            unitTestOverride(false);
            unitTestOverride(false);
            unitTestOverride(false);
            unitTestOverride(false);
            unitTestOverride(false);
            unitTestOverride(false);
            unitTestNonOverride(false);
            unitTestNonOverride(false);
            unitTestNonOverride(false);
            unitTestNonOverride(false);
            unitTestNonOverride(false);
            unitTestNonOverride(false);
            unitTestNonOverride(false);
            unitTestNonOverride(true);
        }
        public static void runStructClassTest()
        {
            #region previous results
            /** 
             * conclusion. Class is faster than struct when passed to a method.
                total time for struct val = 1399080000nsec. Per op = 1399 nsec
                total time for struct val = 1373078500nsec. Per op = 1373 nsec
                total time for struct val = 1374078600nsec. Per op = 1374 nsec
                total time for struct val = 1502085900nsec. Per op = 1502 nsec
                total time for struct ref = 1399080100nsec. Per op = 1399 nsec
                total time for struct ref = 1399080100nsec. Per op = 1399 nsec
                total time for struct ref = 1396079800nsec. Per op = 1396 nsec
                total time for struct ref = 1394079700nsec. Per op = 1394 nsec
                total time for class = 1253071700nsec. Per op = 1253 nsec
                total time for class = 1266072400nsec. Per op = 1266 nsec
                total time for class = 1317075300nsec. Per op = 1317 nsec
                total time for class = 1296074100nsec. Per op = 1296 nsec
                total time for class = 1272072800nsec. Per op = 1272 nsec             
             */
            #endregion

            #region testcode
            unitCheckstructByVal(false);
            unitCheckstructByVal(false);
            unitCheckstructByVal(false);
            unitCheckstructByVal(false);
            unitCheckstructByRef(false);
            unitCheckstructByRef(false);
            unitCheckstructByRef(false);
            unitCheckstructByRef(false);
            unitCheckClass(false);
            unitCheckClass(false);
            unitCheckClass(false);
            unitCheckClass(false);
            unitCheckClass(true);
            #endregion
        }
        public static void runArrayTest()
        {
            #region previous results
            /**
             Conclusion: Jagged array is better for multi dims.
             -------------------------------------------------
              total time nsec for jagged array = 697039900
                total time nsec for jagged array = 683026100
                total time nsec for jagged array = 690229100
                total time nsec for jagged array = 680629400
                total time nsec for jagged array = 689030800
                total time nsec for rect   array = 1142448900
                total time nsec for rect   array = 1142854100
                total time nsec for rect   array = 1141056600
                total time nsec for rect   array = 1142642800
                total time nsec for rect   array = 1139655700
                total time nsec for sing   array = 3217352000
                total time nsec for sing   array = 3213330100
                total time nsec for sing   array = 3219964200
                total time nsec for sing   array = 3220162600
                total time nsec for sing   array = 3228352600
                total time nsec for sing1d array = 618418900
                total time nsec for sing1d array = 619035400
                total time nsec for sing1d array = 622035600
                total time nsec for sing1d array = 621035500
                total time nsec for sing1d array = 628018600
             */
            #endregion

            #region testcode
            unitCheckJagArrays(false);
            unitCheckJagArrays(false);
            unitCheckJagArrays(false);
            unitCheckJagArrays(false);
            unitCheckJagArrays(false);

            unitCheckRecArrays(false);
            unitCheckRecArrays(false);
            unitCheckRecArrays(false);
            unitCheckRecArrays(false);
            unitCheckRecArrays(false);

            unitCheckSingArrays(false);
            unitCheckSingArrays(false);
            unitCheckSingArrays(false);
            unitCheckSingArrays(false);
            unitCheckSingArrays(false);

            unitCheckSingArraysSingAccess(false);
            unitCheckSingArraysSingAccess(false);
            unitCheckSingArraysSingAccess(false);
            unitCheckSingArraysSingAccess(false);
            unitCheckSingArraysSingAccess(true);
            #endregion

        }
        public static void runEnumTest()
        {
            #region result
            /*
             * 4 nano second per enum check and assignment.
             */
            #endregion

            unitCheckEnum(false);
            unitCheckEnum(false);
            unitCheckEnum(false);
            unitCheckEnum(false);
            unitCheckEnum(false);
            unitCheckEnum(false);
            unitCheckEnum(false);
            unitCheckEnum(false);
            unitCheckEnum(true);
        }
        public static void runProjectionTest()
        {
            #region results
            /*
                total time for projection via cos = 61003500nsec. Per op = 61 nsec
                total time for projection via cos = 59003400nsec. Per op = 59 nsec
                total time for projection via cos = 59003400nsec. Per op = 59 nsec
                total time for projection via cos = 59003300nsec. Per op = 59 nsec
                total time for projection via cos = 59003400nsec. Per op = 59 nsec
             */
            #endregion
            unitCheckProjectionCost(false);
            unitCheckProjectionCost(false);
            unitCheckProjectionCost(false);
            unitCheckProjectionCost(false);
            unitCheckProjectionCost(true);
        }
        #endregion

        #region unittests

        #region cast test --------------------------
        public static void unitTestCast(bool block)
        {
            int XIdx, YIdx;
            float X = .5f, Y = .8f;
            int GRIDUNITSIZE = 10;
            int GRIDSIZE = 20;
            getStart();
            for (X = .5f; X < 100000; X++)
            {
                Y++;
                XIdx = (int)Math.Floor((X / (float)GRIDUNITSIZE)) + GRIDSIZE;
                YIdx = (int)Math.Floor((Y / (float)GRIDUNITSIZE)) + GRIDSIZE;
            }
            getDelta();
            writeResult(block, "heaving casting", 100000);
        }

        public static void unitTestNoCast(bool block)
        {
            double XIdx, YIdx;
            float X = .5f, Y = .8f;
            float GRIDUNITSIZE = 10;
            float GRIDSIZE = 20;

            getStart();
            for (X = .5f; X < 100000; X++)
            {
                Y++;
                XIdx = Math.Floor((X / GRIDUNITSIZE)) + GRIDSIZE;
                YIdx = Math.Floor((Y / GRIDUNITSIZE)) + GRIDSIZE;
            }
            getDelta();
            writeResult(block, "no casting", 100000);
        }
        
        #endregion

        #region override method call test ----------------------
        public static void unitTestOverride(bool block)
        {
            BaseClass oClass = new ExtendedClass();
            int b = 0;
            getStart();
            for (int a = 0; a < 30000; a++)
                b = oClass.addNumbers(a, 10);
            getDelta();
            writeResult(block, "extended class method", 30000);

        }

        public static void unitTestNonOverride(bool block)
        {
            NonExtendedClass oClass = new NonExtendedClass();
            int b = 0;
            getStart();
            for (int a = 0; a < 30000; a++)
                b = oClass.addNumbers(a, 10);
            getDelta();
            writeResult(block, "nonextended class method", 30000);

        }
        #endregion

        #region arraytest----------------------------------
        private static void unitCheckJagArrays(bool block)
        {
            int iDim = 10000;
            int[][] a = new int[iDim][];
            int result;
            int idx;
            int idx1;
            for (idx = 0; idx < iDim; idx++)
            {
                a[idx] = new int[iDim];
            }
            getStart();

            for (idx = 0; idx < iDim; idx++)
            {
                for (idx1 = 0; idx1 < iDim; idx1++)
                    result = a[idx][idx1];
            }

            getDelta();
            writeResult(block, "jagged array");
        }
        private static void unitCheckRecArrays(bool block)
        {
            int iDim = 10000;
            int[,] a = new int[iDim, iDim];
            int result;
            int idx;
            int idx1;

            getStart();

            for (idx = 0; idx < iDim; idx++)
            {
                for (idx1 = 0; idx1 < iDim; idx1++)
                    result = a[idx, idx1];
            }
            getDelta();
            writeResult(block, "rect   array");
        }
        private static void unitCheckSingArrays(bool block)
        {
            int iDim = 10000;
            int[] a = new int[iDim * iDim];
            int result;
            int idx;
            int idx1;

            getStart();
            for (idx = 0; idx < iDim; idx++)
            {
                for (idx1 = 0; idx1 < iDim; idx1++)
                    result = a[idx * idx1 + idx1];
            }
            getDelta();
            writeResult(block, "sing   array");
        }
        private static void unitCheckSingArraysSingAccess(bool block)
        {
            int iDim = 10000;
            int iTotalDim = iDim * iDim;
            int[] a = new int[iTotalDim];

            int result;
            int idx;

            getStart();

            for (idx = 0; idx < iTotalDim; idx++)
            {
                result = a[idx];
            }

            getDelta();
            writeResult(block, "sing1d array");
        }
        private static void unitCheckCeiling()
        {
            float flDecimal = 101.2345f;
            double dblResult = 0;
            for (int iDx = 0; iDx < 1000000; iDx++)
            {
                dblResult = Math.Round(flDecimal);
            }
        }
        #endregion-------------------------------

        # region enumtest------------------------
        enum someEnum { item1, item2, item3, itme4, item5, item6 };
        private static void unitCheckEnum(bool block)
        {
            someEnum oEnum = someEnum.itme4;
            getStart();
            for (int iDx = 0; iDx < 10000000; iDx++)
            {
                if (oEnum == someEnum.itme4)
                    oEnum = someEnum.item5;
            }
            getDelta();
            writeResult(block, "Enum test", 10000000);

        }
        #endregion--------------------------

        #region classstruct test-----------------------

        #region helper methods
        private static Week1.world.behavior.DrawableGlobalVar setStructByVal(Week1.world.behavior.DrawableGlobalVar oVars, int iDx)
        {
            for (int iDy = 0; iDy < 200; iDy++)
                oVars.oPosition[iDy].X = iDx;
            return oVars;
        }
        private static Week1.world.behavior.DrawableGlobalVar setStructByRef(ref Week1.world.behavior.DrawableGlobalVar oVars, int iDx)
        {
            for (int iDy = 0; iDy < 200; iDy++)
                oVars.oPosition[iDy].X = iDx;
            return oVars;
        }
        private static void setClass(Week1.world.behavior.DrawableGlobalsClass oVars, int iDx)
        {
            for (int iDy = 0; iDy < 200; iDy++)
                oVars.oPosition[iDy].X = iDx;
        }
        #endregion

        private static void unitCheckstructByRef(bool block)
        {
            getStart();
            // parameter less constructor has same performance. so in this test just using parameter constructor
            Week1.world.behavior.DrawableGlobalVar oVars = new Week1.world.behavior.DrawableGlobalVar(200, "", null, null);
            for (int iDx = 0; iDx < 1000000; iDx++)
            {
                oVars = setStructByRef(ref oVars, iDx);
            }
            getDelta();
            writeResult(block, "struct ref", 1000000);
        }

        private static void unitCheckstructByVal(bool block)
        {
            getStart();
            // parameter less constructor has same performance. so in this test just using parameter constructor
            Week1.world.behavior.DrawableGlobalVar oVars = new Week1.world.behavior.DrawableGlobalVar(200, "", null, null);
            for (int iDx = 0; iDx < 1000000; iDx++)
            {
                oVars = setStructByVal(oVars, iDx);
            }
            getDelta();
            writeResult(block, "struct val", 1000000);
        }

        private static void unitCheckClass(bool block)
        {
            getStart();
            Week1.world.behavior.DrawableGlobalsClass oVars = new Week1.world.behavior.DrawableGlobalsClass(200, "", null, null);
            for (int iDx = 0; iDx < 1000000; iDx++)
            {
                setClass(oVars, iDx);
            }
            getDelta();
            writeResult(block, "class", 1000000);
        }
        #endregion-------------------------------------

        #region projection verctor calculations --------
        public static void unitCheckProjectionCost(bool block)
        {
            double angle = MathHelper.ToRadians(45);
            double mouseY = 100;
            double GroundY;

            getStart();
            for (int iDx = 0; iDx < 1000000; iDx++)
                GroundY = mouseY * (1 / Math.Cos(angle));
            getDelta();
            writeResult(block, "projection via cos", 1000000);
        }
        #endregion -------------------------------------

        #endregion
    }

    #region override method cost testing
    abstract public class BaseClass
    {
        abstract public int addNumbers(int a, int b);
    }

    public class ExtendedClass : BaseClass
    {
        public override int addNumbers(int a, int b)
        {
            return a + b;
        }
    }

    public class NonExtendedClass
    {
        public int addNumbers(int a, int b)
        {
            return a + b;
        }
    }
    #endregion

}
